home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc Development Framework / ODFDev / ODF / Found / FWExcLib / FWExcImp.cpp < prev    next >
Encoding:
Text File  |  1996-09-17  |  24.2 KB  |  755 lines  |  [TEXT/MPS ]

  1. //========================================================================================
  2. //
  3. //    File:                FWExcImp.cpp
  4. //    Release Version:    $ ODF 2 $
  5. //
  6. //    Copyright:    (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  7. //
  8. //========================================================================================
  9.  
  10. #ifndef FW_NATIVE_EXCEPTIONS
  11.  
  12. #include "FWFound.hpp"
  13.  
  14. #ifndef FWEXCIMP_H
  15. #include "FWExcImp.h"
  16. #endif
  17.  
  18. #ifndef FWDEBUG_H
  19. #include "FWDebug.h"
  20. #endif
  21.  
  22. #ifdef FW_BUILD_MAC
  23. #include <Gestalt.h>
  24. #endif
  25.  
  26. #ifdef FW_BUILD_MAC
  27. #pragma segment slexcept
  28. #endif
  29.  
  30. #ifdef FW_DEBUG
  31.     #include <stdio.h>
  32.     #define _FW_NAME_PARAMETER ,char* name
  33. #else
  34.     #define _FW_NAME_PARAMETER
  35. #endif
  36.  
  37. //========================================================================================
  38. // struct FW_SExceptionGlobals
  39. //========================================================================================
  40.  
  41. struct FW_SExceptionGlobals
  42. {
  43.     FW_SPrivTryBlockContext*    fCurrentContext;
  44.     FW_SPrivWatcher*             fWatchList;
  45.     FW_SPrivDeleteElem*            fStackBottom;    // These are pointers into the delete stack,
  46.     FW_SPrivDeleteElem*            fStackTop;        // not pointers into the function call stack
  47.     FW_SPrivDeleteElem*            fStackLimit;
  48.  
  49.     void*                        fExceptionBuffer;
  50.     size_t                        fExceptionBufferSize;
  51.     
  52.     FW_SPrivExceptionInfo        fCaughtException;
  53.     FW_SPrivExceptionInfo         fThrownException;
  54.     
  55.     FW_Boolean                    fIsUnwinding;
  56.     
  57.     void*                        fCallStackBase;
  58.     void*                        fProcessStackBase;
  59.     FW_PlatformThreadID            fThreadID;
  60. };
  61.  
  62. static FW_SExceptionGlobals     gMainThreadExceptionData = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  63. static FW_SExceptionGlobals*    gExceptionData = &gMainThreadExceptionData;
  64.  
  65. // FW_gInStaticInit is initialized to a special magic number before static initialization 
  66. // here.  It should be cleared by the application main or shared library initialization
  67. // routine (CFMInit) immediately after static initialization has been performed, and before
  68. // the first try block or construction of an autodestruct object with auto scope or
  69. // dynamic scope.  Note that this mechanism precludes placing
  70. // this exception emulation system in a shared library that other clients could use.
  71. // Currently, every shared library component must statically link in this eumulation
  72. // system.  However, we did have this system working from a shared library, and the
  73. // use of FW_gInStaticInit is the only thing that we have done since then that would 
  74. // prevent it from moved back into a shared library. [JEL, ODF R1, 3/21/96]
  75.  
  76. const long kMagicCookie = 'jiml';
  77. long FW_gInStaticInit = kMagicCookie;
  78.  
  79. void* FW_gPrivVolatileKludge = 0;
  80.  
  81. //========================================================================================
  82. // Key Utility Functions
  83. //========================================================================================
  84.  
  85. static FW_Boolean FW_PrivHasThreadManager()
  86. {
  87.     static FW_Boolean gHasFeature = FALSE;
  88.  
  89. #ifdef FW_BUILD_MAC
  90.     static FW_Boolean gChecked = FALSE;
  91.     
  92.     if (!gChecked)
  93.     {
  94.         long response;
  95.         
  96.         gHasFeature = ((Gestalt(gestaltThreadMgrAttr, &response) == noErr) && (response & (1 << gestaltThreadMgrPresent)));
  97.         gHasFeature &= (&GetCurrentThread != (void*)kUnresolvedCFragSymbolAddress);
  98.         gChecked = TRUE;
  99.     }
  100. #endif
  101.  
  102.     return gHasFeature;
  103. }
  104.  
  105. static FW_PlatformThreadID FW_PrivGetCurrentThread()
  106. {
  107.     FW_PlatformThreadID    currentThread = 0;
  108.  
  109. #ifdef FW_BUILD_MAC
  110.     if (FW_PrivHasThreadManager())
  111.     {
  112.         FW_PlatformError err = GetCurrentThread(¤tThread);
  113.         if (err != FW_xNoError)
  114.         {
  115.             FW_DEBUG_MESSAGE("FWExcImp::FW_PrivGetCurrentThread failed");
  116.             currentThread = 0;
  117.         }
  118.     }
  119. #endif
  120.     return currentThread;
  121. }
  122.  
  123. static long AddressIsOnStack(void* p)
  124. {
  125.     long local;
  126.     FW_PRIV_ASSERT(gExceptionData);
  127.     FW_PRIV_ASSERT(gExceptionData->fCurrentContext);
  128.     return (p > &local) && p <= gExceptionData->fCallStackBase;
  129. }
  130.  
  131. static long IsCurrentSubObject(void* p)
  132. {
  133.     register FW_SPrivWatcher* w = gExceptionData->fWatchList;
  134.     return w && p>=w->fObject && p<w->fLimit;
  135. }
  136.  
  137. static long IsCurrentObject(void* p, void* limit)
  138. {
  139.     register FW_SPrivWatcher* w = gExceptionData->fWatchList;
  140.     return w && p==w->fObject && limit==w->fLimit;
  141. }
  142.  
  143. static long TrackThisObject(void* object)
  144. {
  145.     FW_PRIV_ASSERT(gExceptionData->fCurrentContext);
  146.     return AddressIsOnStack(object) || IsCurrentSubObject(object);
  147. }
  148.  
  149. static void DoTerminate()
  150. {
  151.     // If we get here, we either have a throw with no try block in scope,
  152.     // or a rethrow with no exception in scope.
  153.     extern void terminate();
  154.     FW_DEBUG_MESSAGE("Exception error, calling terminate()");
  155. #if defined(__SC__) || defined(__MRC__)
  156.     // Symantec & MrC currently don't have terminate() in their runtime library.
  157.     ExitToShell();
  158. #else
  159.     terminate();
  160. #endif
  161. }
  162.  
  163. static void InitializeExceptionGlobals(FW_SExceptionGlobals* globals, void* bottom)
  164. {
  165.     const int kInitialStackSize = 1000;
  166.     const int kExceptionBufferSize = 256;
  167.  
  168.     globals->fProcessStackBase = LMGetCurStackBase();
  169.     globals->fCallStackBase = (globals == &gMainThreadExceptionData) ? globals->fProcessStackBase : bottom;
  170.     globals->fThreadID = FW_PrivGetCurrentThread();
  171.     globals->fStackTop = globals->fStackBottom = (FW_SPrivDeleteElem*)FW_PrimitiveAllocateBlock(kInitialStackSize*sizeof(FW_SPrivDeleteElem));
  172.     globals->fStackLimit = globals->fStackBottom + kInitialStackSize;
  173.  
  174.     globals->fExceptionBuffer = FW_PrimitiveAllocateBlock(kExceptionBufferSize);
  175.     globals->fExceptionBufferSize = kExceptionBufferSize;
  176. }
  177.  
  178. static void FinalizeExceptionGlobals(FW_SExceptionGlobals* globals)
  179. {
  180.     FW_PrimitiveFreeBlock(globals->fStackBottom);
  181.     globals->fStackTop = globals->fStackBottom = 0;
  182.     globals->fThreadID = 0;
  183.     
  184.     FW_PrimitiveFreeBlock(globals->fExceptionBuffer);
  185.     globals->fExceptionBuffer = 0;
  186. }
  187.  
  188. //========================================================================================
  189. // DeleteElement Functions
  190. //========================================================================================
  191.  
  192. static void DeleteElem_DestroyObject(FW_SPrivDeleteElem* elem)
  193. {
  194.     FW_PRIV_ASSERT(gExceptionData);
  195.     FW_PRIV_ASSERT(gExceptionData->fIsUnwinding);
  196.     FW_PRIV_ASSERT(elem);
  197.     FW_PRIV_ASSERT(elem->fDestroyer);
  198.     FW_PRIV_ASSERT(elem->fObject);
  199.     (*elem->fDestroyer)(elem->fObject);
  200.     FW_PRIV_ASSERT(!elem->fObject);
  201. }
  202.  
  203. //========================================================================================
  204. // ExceptionInfo Functions
  205. //========================================================================================
  206.  
  207. static void ExceptionInfo_Clear(FW_SPrivExceptionInfo& exception)
  208. {
  209.     exception.fAddress = 0;
  210.     
  211.     // The remainder don't have to be cleared, but we do so anyway
  212.     exception.fSize = 0;
  213.     exception.fClass = 0;
  214.     exception.fDestroyer = 0;
  215.     exception.fCloner = 0;
  216. }
  217.  
  218. static void ExceptionInfo_DestroyException(FW_SPrivExceptionInfo& exception)
  219. {
  220.     FW_PRIV_ASSERT(exception.fDestroyer);
  221.     FW_PRIV_ASSERT(exception.fAddress);
  222.     (*exception.fDestroyer)(exception.fAddress);
  223.     ExceptionInfo_Clear(exception);
  224. }
  225.  
  226. static void ExceptionInfo_Set(FW_SPrivExceptionInfo& exception,
  227.                                 void* address, 
  228.                                 size_t size, 
  229.                                 FW_SClassInfoPtr classPtr, 
  230.                                 FW_PrivDestroyProc destroyer,
  231.                                 FW_PrivCloneProc cloner)
  232. {
  233.     exception.fAddress = address;
  234.     exception.fSize = size;
  235.     exception.fClass = classPtr;
  236.     exception.fDestroyer = destroyer;
  237.     exception.fCloner = cloner;
  238. }
  239.  
  240. //========================================================================================
  241. // DeleteStack Functions
  242. //========================================================================================
  243.  
  244. static void DeleteStack_ResetTop()
  245. {
  246.     FW_PRIV_ASSERT(gExceptionData);
  247.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  248.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  249.     FW_PRIV_ASSERT(gExceptionData->fCurrentContext);
  250.     FW_SPrivDeleteElem* elem = gExceptionData->fStackTop-1;
  251.     FW_SPrivDeleteElem* last = gExceptionData->fStackBottom
  252.                              + gExceptionData->fCurrentContext->fDeleteStackLevel;
  253.     while (elem>=last && !elem->fObject)
  254.         --elem;
  255.     gExceptionData->fStackTop = elem+1;
  256. }
  257.  
  258. static void DeleteStack_Push(void* object, FW_PrivDestroyProc destroyer _FW_NAME_PARAMETER)
  259. {
  260.     FW_PRIV_ASSERT(gExceptionData);
  261.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  262.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  263.     FW_PRIV_ASSERT(gExceptionData->fStackTop < gExceptionData->fStackLimit);
  264.     gExceptionData->fStackTop->fObject = object;
  265.     gExceptionData->fStackTop->fDestroyer = destroyer;
  266. #ifdef FW_DEBUG
  267.     gExceptionData->fStackTop->fName = name;
  268.     gExceptionData->fStackTop->fHeapAllocated = IsCurrentSubObject(object);
  269.     if (gExceptionData->fStackTop->fHeapAllocated)
  270.     {
  271.         register FW_SPrivWatcher* w = gExceptionData->fWatchList;
  272.         w->fDeleteElems++;
  273.     }
  274. #endif
  275.     gExceptionData->fStackTop++;
  276. }
  277.  
  278. static void DeleteStack_ClearHeapObjectElements()
  279. {
  280.     // Called as last step of FW_NEW
  281.     FW_PRIV_ASSERT(gExceptionData);
  282.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  283.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  284.     FW_PRIV_ASSERT(gExceptionData->fStackTop < gExceptionData->fStackLimit);
  285.     FW_PRIV_ASSERT(gExceptionData->fWatchList);
  286.     FW_PRIV_ASSERT(gExceptionData->fCurrentContext);
  287.     FW_SPrivWatcher* w = gExceptionData->fWatchList;
  288.     FW_SPrivDeleteElem* elem = gExceptionData->fStackTop-1;
  289.     FW_SPrivDeleteElem* last = gExceptionData->fStackBottom
  290.                              + gExceptionData->fCurrentContext->fDeleteStackLevel;
  291.     
  292.     while (elem>=last)
  293.     {
  294.         if (elem->fObject >= w->fObject && elem->fObject < w->fLimit)
  295.         {
  296.             FW_PRIV_ASSERT(elem->fHeapAllocated);
  297.             elem->fObject = 0;
  298. #ifdef FW_DEBUG
  299.             w->fDeleteElems--;
  300. #endif
  301.         }
  302.         --elem;
  303.     }
  304.     FW_PRIV_ASSERT(w->fDeleteElems == 0);
  305.     DeleteStack_ResetTop();
  306. }
  307.  
  308. //========================================================================================
  309. // TryBlockContext functions
  310. //========================================================================================
  311.  
  312. static void TryBlockContext_UnwindStack(FW_SPrivTryBlockContext* context)
  313. {
  314.     FW_PRIV_ASSERT(context);
  315.     FW_PRIV_ASSERT(gExceptionData);
  316.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  317.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  318.  
  319.     gExceptionData->fIsUnwinding = true;
  320.  
  321.     FW_SPrivDeleteElem* last = gExceptionData->fStackBottom + context->fDeleteStackLevel;
  322.     FW_PRIV_ASSERT(gExceptionData->fStackTop >= last);
  323.     
  324.     while (gExceptionData->fStackTop > last)
  325.     {
  326.         DeleteElem_DestroyObject(gExceptionData->fStackTop-1);
  327.         // The destructor of the deleted object will have popped one or more elements 
  328.         // off of the stack so fStackTop will point to the new stack top.
  329.     }
  330.  
  331.     FW_PRIV_ASSERT(gExceptionData->fStackTop == last);
  332.     gExceptionData->fIsUnwinding = false;
  333. }
  334.  
  335. inline void TryBlockContext_LongJump(FW_SPrivTryBlockContext* context)
  336. {
  337.      (*context->fJumpProc)(*context->fJumpBuffer, 1);
  338.      FW_PRIV_ASSERT(false);    // should never return from above function
  339. }
  340.  
  341. static char* gContextName = "TryBlockContext";
  342.  
  343. void FW_PrivTryBlockContext_Init(FW_SPrivTryBlockContext *self, jmp_buf buffer, FW_PrivLongJumpProc jumpProc)
  344. {
  345.     if (!gExceptionData->fStackBottom)
  346.         InitializeExceptionGlobals(gExceptionData, self);
  347.  
  348.     self->fPriorStackBase = gExceptionData->fCallStackBase;
  349.     self->fPriorProcessBase = gExceptionData->fProcessStackBase;
  350.     self->fPriorThreadID = gExceptionData->fThreadID;
  351.  
  352.     void* currentStackBase = LMGetCurStackBase();
  353.     FW_PlatformThreadID currentThread = FW_PrivGetCurrentThread();
  354.     if (currentStackBase != gExceptionData->fProcessStackBase || currentThread != gExceptionData->fThreadID)
  355.     {
  356.         //    This code will execute in two cases:
  357.         //        1. We've been called from a different process. This might happen during drag and drop.
  358.         //        2. We've been called from a different thread and, consequently, stack-based object
  359.         //           will be created on a different stack.
  360.         //
  361.         //    In the case of a different thread, LMGetCurStackBase returns the base of the stack
  362.         //    belonging to the primary execution thread - not the stack of the currently
  363.         //    executing thread. Consequently, we have to test both stackbase and current thread
  364.         //    to detect stack swaps.
  365.         gExceptionData->fCallStackBase = self;
  366.         gExceptionData->fProcessStackBase = currentStackBase;
  367.         gExceptionData->fThreadID = currentThread;
  368.     }
  369.  
  370.     self->fJumpBuffer = (jmp_buf *) buffer;
  371.     self->fJumpProc = jumpProc;
  372.     self->fDeleteStackLevel = gExceptionData->fStackTop - gExceptionData->fStackBottom;
  373.     self->fPriorContext = FW_PrivTryBlockContext_MakeCurrent(self);
  374. #ifdef FW_DEBUG
  375.     FW_AutoConstructed(self, sizeof(FW_SPrivTryBlockContext), FW_PrivTryBlockContext_Destroy, gContextName);
  376. #else
  377.     FW_AutoConstructed(self, sizeof(FW_SPrivTryBlockContext), FW_PrivTryBlockContext_Destroy);
  378. #endif
  379. }
  380.  
  381. void FW_PrivTryBlockContext_Destroy(void* context)
  382. {
  383. #ifdef FW_DEBUG
  384.     FW_AutoDestructed(context, FW_PrivTryBlockContext_Destroy, gContextName);
  385. #else
  386.     FW_AutoDestructed(context);
  387. #endif
  388.     FW_SPrivTryBlockContext* self = (FW_SPrivTryBlockContext*) context;
  389.     
  390.     gExceptionData->fCallStackBase = self->fPriorStackBase;
  391.     gExceptionData->fProcessStackBase = self->fPriorProcessBase;
  392.     gExceptionData->fThreadID = self->fPriorThreadID;
  393.  
  394. #ifdef FW_DEBUG
  395.     while (gExceptionData->fStackTop != gExceptionData->fStackBottom+self->fDeleteStackLevel)
  396.     {
  397.         FW_SPrivDeleteElem* elem = --gExceptionData->fStackTop;
  398.         if (elem->fObject != 0)
  399.         {
  400.             // If one of the above test fails, one or more objects were not removed off of
  401.             // the delete stack by the time the try block went out of scope.  This is almost
  402.             // certainly because some class has an FW_END_CONSTRUCTOR without a matching FW_START_DESTRUCTOR.
  403.             char message[256];
  404.             sprintf(message, "The autodestruct class %s is missing an FW_START_DESTRUCTOR", elem->fName);
  405.             FW_DEBUG_MESSAGE(message);
  406.         }
  407.     }
  408. #endif
  409.     
  410.     FW_PRIV_ASSERT(self->fDeleteStackLevel == gExceptionData->fStackTop - gExceptionData->fStackBottom);
  411.     FW_SPrivTryBlockContext* priorContext = FW_PrivTryBlockContext_MakeCurrent(self->fPriorContext);
  412.     FW_PRIV_ASSERT(priorContext == self);
  413. }
  414.  
  415. FW_SPrivTryBlockContext* FW_PrivTryBlockContext_MakeCurrent(FW_SPrivTryBlockContext* context)
  416. {
  417.     FW_SPrivTryBlockContext *tmpContext = gExceptionData->fCurrentContext;
  418.     gExceptionData->fCurrentContext = context;
  419.     return tmpContext;
  420. }
  421.  
  422. //========================================================================================
  423. // FW_SPrivWatcher Functions
  424. //========================================================================================
  425.  
  426. static void Watcher_Push(FW_SPrivWatcher *self, void* obj, size_t size)
  427. {
  428.     self->fObject = obj;
  429.     self->fLimit = (char*)obj + size;
  430.     self->fNext = gExceptionData->fWatchList;
  431.     gExceptionData->fWatchList = self;
  432. }
  433.  
  434. static char* gWatcherName = "Watcher";
  435.  
  436. void FW_PrivWatcher_Init(FW_SPrivWatcher* self, FW_PrivDeleteProc deleter)
  437. {
  438.     self->fNext = 0;
  439.     self->fObject = 0;
  440.     self->fLimit = 0;
  441.     self->fDeleter = deleter;
  442. #ifdef FW_DEBUG
  443.     self->fDeleteElems = 0;
  444.     FW_AutoConstructed(self, sizeof(FW_SPrivWatcher), FW_PrivWatcher_Destroy, gWatcherName);
  445. #else
  446.     FW_AutoConstructed(self, sizeof(FW_SPrivWatcher), FW_PrivWatcher_Destroy);
  447. #endif
  448. }
  449.  
  450. void* FW_PrivWatcher_Pop()
  451. {
  452.     FW_PRIV_ASSERT(gExceptionData);
  453.     FW_PRIV_ASSERT(gExceptionData->fWatchList);
  454.     FW_PRIV_ASSERT(gExceptionData->fWatchList->fObject);
  455.     void* result = gExceptionData->fWatchList->fObject;
  456.     
  457.     if (gExceptionData->fCurrentContext)
  458.         DeleteStack_ClearHeapObjectElements();
  459.  
  460.     gExceptionData->fWatchList->fObject = 0;
  461.     gExceptionData->fWatchList = gExceptionData->fWatchList->fNext;
  462.     return result;
  463. }
  464.  
  465. void FW_PrivWatcher_Destroy(void* self)
  466. {
  467. #ifdef FW_DEBUG
  468.     FW_AutoDestructed(self, FW_PrivWatcher_Destroy, gWatcherName);
  469. #else
  470.     FW_AutoDestructed(self);
  471. #endif
  472.     if (gExceptionData->fWatchList == self)
  473.     {
  474.         FW_SPrivWatcher* me = (FW_SPrivWatcher*) self;
  475.         FW_PRIV_ASSERT(me->fObject);
  476.         FW_PrimitiveFreeBlock(me->fObject);
  477.         gExceptionData->fWatchList = me->fNext;
  478.     }
  479. }
  480.  
  481. void* FW_PrivWatcher_New(void* p, size_t size, FW_SPrivWatcher* self)
  482. {
  483.     Watcher_Push(self, p, size);
  484.     return p;
  485. }
  486.  
  487. //========================================================================================
  488. // Global Functions
  489. //========================================================================================
  490.  
  491. void FW_AutoConstructed(void* object, size_t, FW_PrivDestroyProc destroyer _FW_NAME_PARAMETER)
  492. {
  493.     if (FW_gInStaticInit == kMagicCookie)
  494.         return;
  495.  
  496.     FW_PRIV_ASSERT(gExceptionData);
  497.     FW_PRIV_ASSERT(("Error: an autodestruct object constructed without an enclosing a FW_TRY block", gExceptionData->fCurrentContext!=NULL));
  498.  
  499.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  500.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  501.  
  502.     if (TrackThisObject(object))
  503.     {
  504. #ifdef FW_DEBUG
  505.         DeleteStack_Push(object, destroyer, name);
  506. #else
  507.         DeleteStack_Push(object, destroyer);
  508. #endif
  509.     }
  510.     else
  511.     {
  512. #ifdef FW_DEBUG
  513.         char buffer[256];
  514.         sprintf(buffer, "Why is an autodestruct object of class %s being constructed in a bad context?  See FWExcImp.cpp for possible causes.", name);
  515.         FW_DEBUG_MESSAGE(buffer);
  516.         // Possible causes:
  517.         // 1) The autodestruct object is a member of another object which itself 
  518.         // is not an autodestruct object. This other object is being
  519.         // allocated via new, not FW_NEW.  If so, this is a programmer error.
  520.         // Make the other object's class be autodestruct, and use FW_NEW.
  521.         // 2) This is a static object of a shared library that has just been
  522.         // loaded, and static constructors are being executed.  We have attempted
  523.         // to detect this condition and not report it as an error, but may not
  524.         // have eliminated it for all environments.  If this is the case, it
  525.         // is not a programming error.  You can safely continue execution.
  526.         // Note: if you move this code into an application (as opposed to a 
  527.         // shared library), you may need to set FW_gInStaticInit to zero
  528.         // in your main() to inform this subsystem that static initialization
  529.         // is complete.
  530.         // 3) A case similar to 2) is possible.  If a static autodestruct object
  531.         // is declared in a function, the compiler is not obligated to initialize
  532.         // it at static initialization time, it can (and should) wait until the function is
  533.         // first called.  This case is very difficult to detect (without help
  534.         // from the compiler!).  It is not a programming error, and it is safe
  535.         // to continue excecution.
  536. #endif
  537.     }
  538. }
  539.  
  540. void FW_AutoDestructed(
  541.     void* object
  542. #ifdef FW_DEBUG
  543.     , FW_PrivDestroyProc destroyer
  544.     , char* name
  545. #endif
  546.     )
  547. {
  548.     if (FW_gInStaticInit == kMagicCookie)
  549.         return;
  550.  
  551.     FW_PRIV_ASSERT(gExceptionData);
  552.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  553.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  554.     
  555.     if (gExceptionData->fCurrentContext)
  556.     {
  557.         if (TrackThisObject(object))
  558.         {
  559.             FW_SPrivDeleteElem* elem = gExceptionData->fStackTop-1;
  560.             FW_SPrivDeleteElem* last = gExceptionData->fStackBottom
  561.                                      + gExceptionData->fCurrentContext->fDeleteStackLevel;
  562.             
  563.             // Search for the correct entry on the delete stack.
  564.             // Normally this will be the first entry, but not always.
  565.             while (elem>=last && elem->fObject!=object)
  566.                 --elem;
  567.             
  568. #ifdef FW_DEBUG
  569.             // If any of the following tests fail, we didn't find the object being destructed
  570.             // in our delete stack.  Probable cause is a FW_START_DESTRUCTOR without
  571.             // matching FW_END_CONSTRUCTOR.
  572.             char message[256];
  573.             if (elem<last)
  574.             {
  575.                 sprintf(message, "The autodestruct class %s is missing an FW_END_CONSTRUCTOR.", name);
  576.                 FW_DEBUG_MESSAGE(message);
  577.             }
  578.             else if (elem->fObject != object || elem->fDestroyer != destroyer)
  579.             {
  580.                 sprintf(message, "An autodestruct class is missing an FW_END_CONSTRUCTOR or FW_START_DESTRUCTOR. Likely candidates: %s and %s respectively.", name, elem->fName);
  581.                 FW_DEBUG_MESSAGE(message);
  582.             }
  583. #endif
  584.             
  585.             // Objects aren't necessarily removed in the order they were pushed.
  586.             // We simply clear entries on the stack, and then reset the stack top
  587.             // to be one past the last active item on the stack.
  588.             elem->fObject = 0;
  589.             DeleteStack_ResetTop();
  590.         }
  591.     }
  592. }
  593.  
  594. void* FW_PrivGetThrownException()
  595. {
  596.     return gExceptionData->fThrownException.fAddress;
  597. }
  598.  
  599. void FW_PrivCaughtException(void* exception, size_t size, FW_SClassInfoPtr itsClass, FW_PrivDestroyProc itsDestroyer, FW_PrivCloneProc itsCloner)
  600. {
  601.     // We've caught an exception by value, and possibly sliced the object.
  602.     // We like to not clear the fThrownException in case we do a FW_THROW_SAME.
  603.     // Unfortunately, we don't work that way right now !!! •••JEL
  604.     FW_PRIV_ASSERT(gExceptionData);
  605.     FW_PRIV_ASSERT(!gExceptionData->fCaughtException.fAddress);
  606.     ExceptionInfo_Set(gExceptionData->fCaughtException, exception, size, itsClass, itsDestroyer, itsCloner);
  607.     ExceptionInfo_Clear(gExceptionData->fThrownException);
  608. }
  609.  
  610. void FW_PrivCaughtNoInstanceException()
  611. {
  612.     // We've caught an exception by name only
  613.     FW_PRIV_ASSERT(gExceptionData);
  614.     gExceptionData->fCaughtException = gExceptionData->fThrownException;
  615.     ExceptionInfo_Clear(gExceptionData->fThrownException);
  616. }
  617.  
  618. void FW_PrivCaughtEverythingException()
  619. {
  620.     // We've caught any kind of exception
  621.     FW_PrivCaughtNoInstanceException();
  622. }
  623.  
  624. void FW_PrivCaughtReferenceException()
  625. {
  626.     // We've caught an exception by reference
  627.     FW_PrivCaughtNoInstanceException();
  628. }
  629.  
  630. void FW_PrivCatchCleanup()
  631. {
  632.     FW_PRIV_ASSERT(gExceptionData);
  633.     ExceptionInfo_DestroyException(gExceptionData->fCaughtException);
  634.     
  635.     // The thrown exception already be cleared
  636.     FW_PRIV_ASSERT(gExceptionData->fThrownException.fAddress == 0);
  637. }
  638.  
  639. void FW_PrivKeepThrowing()
  640. {
  641.     // An exception didn't match any of the Catch clauses, so it
  642.     // needs to be propogated up to the next try block
  643.     FW_PRIV_ASSERT(gExceptionData);
  644.     FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer);
  645.     FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer == gExceptionData->fThrownException.fAddress);
  646.  
  647.     FW_SPrivTryBlockContext *context = gExceptionData->fCurrentContext;
  648.     FW_PRIV_ASSERT(context != NULL);
  649.     
  650.     TryBlockContext_UnwindStack(context);
  651.     TryBlockContext_LongJump(context); // LongJump doesn't return
  652. }
  653.  
  654.  
  655. FW_Boolean FW_PrivCanCatchThisException(FW_SClassInfoPtr targetClass)
  656. {
  657.     FW_PRIV_ASSERT(gExceptionData);
  658.     
  659.     void* address = gExceptionData->fThrownException.fAddress;
  660.     FW_PRIV_ASSERT(address);
  661.     
  662.     FW_SClassInfoPtr fromClass = gExceptionData->fThrownException.fClass;
  663.     
  664.     return 0 != FW_PrivDynamicCast(address, fromClass, fromClass, targetClass);
  665. }
  666.  
  667. void FW_PrivThrow(void* exception, size_t size, FW_SClassInfoPtr itsClass, FW_PrivDestroyProc itsDestroyer, FW_PrivCloneProc itsCloner)
  668. {
  669.     FW_PRIV_ASSERT(exception);
  670.     FW_PRIV_ASSERT(size);
  671.     FW_PRIV_ASSERT(itsClass);
  672.     FW_PRIV_ASSERT(itsDestroyer);
  673.     FW_PRIV_ASSERT(itsCloner);
  674.  
  675.     FW_PRIV_ASSERT(gExceptionData);
  676.     FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer);
  677.     FW_PRIV_ASSERT(gExceptionData->fExceptionBufferSize);
  678.     FW_PRIV_ASSERT(size <= gExceptionData->fExceptionBufferSize);
  679.  
  680.     FW_PRIV_ASSERT(!gExceptionData->fIsUnwinding);
  681.     if (gExceptionData->fIsUnwinding)
  682.     {
  683.         // Can't throw exception while unwinding stack from previous exception
  684.         DoTerminate();
  685.     }
  686.  
  687.     // fThrownException is non-null only between throw and catch
  688.     // If it is non-null on entry to PrivThrow then something is horribly wrong
  689.     FW_PRIV_ASSERT(gExceptionData->fThrownException.fAddress == NULL);
  690.     
  691.     FW_SPrivTryBlockContext *context = gExceptionData->fCurrentContext;
  692.     if (context == NULL)
  693.         DoTerminate();
  694.  
  695.     // If we're in a catch block and we throw then there
  696.     // will be a caught exception that must be destroyed before
  697.     // we write over it with the new exception.
  698.     if (gExceptionData->fCaughtException.fAddress != NULL)
  699.         ExceptionInfo_DestroyException(gExceptionData->fCaughtException);
  700.  
  701.     // Copy the original into our exception buffer
  702.     (*itsCloner)(exception, gExceptionData->fExceptionBuffer, size);
  703.  
  704.     // We have copied the original so delete it.
  705.     (*itsDestroyer)(exception);
  706.  
  707.     // We have a thrown exception so point gThrownException at it.
  708.     ExceptionInfo_Set(gExceptionData->fThrownException, 
  709.                         gExceptionData->fExceptionBuffer, 
  710.                         size, 
  711.                         itsClass, 
  712.                         itsDestroyer, 
  713.                         itsCloner);
  714.  
  715.     TryBlockContext_UnwindStack(context);
  716.     TryBlockContext_LongJump(context);    // doesn't return
  717. }
  718.  
  719. void FW_PrivThrowSame()
  720. {
  721.     FW_PRIV_ASSERT(gExceptionData);
  722.     FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer);
  723.  
  724.     FW_PRIV_ASSERT(gExceptionData->fCaughtException.fAddress);
  725.     if (gExceptionData->fCaughtException.fAddress == NULL)
  726.     {
  727.         // can't rethrow if no exception is in scope
  728.         DoTerminate();
  729.     }
  730.  
  731.     FW_PRIV_ASSERT(!gExceptionData->fIsUnwinding);
  732.     if (gExceptionData->fIsUnwinding)
  733.     {
  734.         // Can't throw exception while unwinding stack from previous exception
  735.         DoTerminate();
  736.     }
  737.  
  738.     FW_SPrivTryBlockContext *context = gExceptionData->fCurrentContext;
  739.     FW_PRIV_ASSERT(context != NULL);
  740.     if (context == NULL)
  741.         DoTerminate();
  742.  
  743.     gExceptionData->fThrownException = gExceptionData->fCaughtException;
  744.     ExceptionInfo_Clear(gExceptionData->fCaughtException);
  745.  
  746.     TryBlockContext_UnwindStack(context);
  747.     TryBlockContext_LongJump(context);
  748.     
  749.     // LongJump doesn't return, we should never get to here
  750.     FW_PRIV_ASSERT(false);
  751. }
  752.  
  753. #endif // FW_NATIVE_EXCEPTIONS
  754.  
  755.